#include "Team.h"
#include "Utilities.h"
#include "PeopleManager.h"
#include "ServiceProvider.h"
#include "ZStream.h"
#include "People.h"
#include "NameManager.h"
#include "LeagueManager.h"
#include "PeopleFunctors.h"
#include "GameSettings.h"
#include "RandomSystem.h"

TeamParams Team::s_params;

const int Team::TITOLAR_PER_ROLE_BY_FORMATION[NUM_TEAM_FORMATIONS][NUM_FORMATION_ROLES] =
{
	{1,2,0,2}, //TEAM_FORMATION_202
	{1,1,2,1}, //TEAM_FORMATION_121
	{1,0,4,0}  //TEAM_FORMATION_040
};

const int Team::ROLE_ROLE_PERCENT_PLAYING[NUM_TEAM_FORMATIONS][NUM_FORMATION_ROLES][NUM_SKILL_ROLES] =
{
	//TEAM_FORMATION_202
	{
		{100, 25, 25}, //GoalkeeperRole		(1)
		{  0,100, 50}, //DefenderRole		(2)
		{  0,  0,  0}, //MidfieldRole		(0)
		{  0, 50,100}  //AttackerRole		(2)
	},

	//TEAM_FORMATION_121
	{
		{100, 25, 25}, //GoalkeeperRole		(1)
		{  0,120, 30}, //DefenderRole		(1)
		{  0, 80, 80}, //MidfieldRole		(2)
		{  0, 30,120}  //AttackerRole		(1)
	},

	//TEAM_FORMATION_040
	{
		{100, 25, 25}, //GoalkeeperRole		(1)
		{  0,  0,  0}, //DefenderRole		(0)
		{  0, 80, 80}, //MidfieldRole		(4)
		{  0,  0,  0}  //AttackerRole		(0)
	}
};

#pragma warning(push)
#pragma warning(disable: 4355)

Team::Team(ServiceProvider* const i_sp, const STL::string& i_name, const int i_id) 
	:m_name(i_name)
	,m_userControlled(false)
	,m_id(i_id)
	,m_leagueID(Utilities::INVALID_ID)
	,m_sp(i_sp)
	,m_ai(i_sp, this)
	,m_budget(0)
	,m_bankruptWarnings(0)
	,m_formation(TEAM_FORMATION_202)
{
	assert(i_sp);
	m_teamHistory.Init(m_sp->GetLeagueMgr().GetNumOfLayers(false), m_sp->GetSettings().GetValue("I_STARTING_YEAR"));
	
	for(size_t i=0; i<Team::NUM_TRAINERS_ROLES;++i)
		m_trainerRoles[i] = 0;
}

#pragma warning(pop)

bool Team::AddPeople(People* i_people, const bool i_createHistoryRecord)
{
	PeopleVec& theVec = i_people->IsPlayer() ? m_players : m_trainers;
	if (theVec.size() < s_params.GetMaxPeople(i_people->IsPlayer()))
	{	
		theVec.push_back(i_people);
		i_people->SetTeamID(m_id);

#ifndef DARIO
		// Set contract
		i_people->SetContract(i_people->CalculateExpectedSalary(), Rand()%5+1);
#endif

		if(i_createHistoryRecord)
			m_sp->GetPeopleMgr().CreateHistoryEntry(i_people);
		return true;
	}
	return false;
}

bool Team::RemovePeople(People* i_people)
{
	PeopleVec& theVec = i_people->IsPlayer() ? m_players : m_trainers;
	if (theVec.size() <= s_params.GetMinPeople(i_people->IsPlayer()))
		return false;

	PeopleVecIt it = STL::find(theVec.begin(), theVec.end(), i_people);
	if(it == theVec.end())
		return false;

	(*it)->SetTeamID(Utilities::INVALID_ID);
	theVec.erase(it);
	return true;
}

bool Team::BuyPlayer(People* i_player, const size_t price, const size_t years)
{
	assert(i_player);

	// check if the team can buy the player
	if((m_budget <= static_cast<int>(price)) && (GetNumPlayers() > s_params.MIN_TEAM_PLAYERS))
		return false;

	const bool result = AddPeople(i_player);

	if(result)
	{
		// update the budget
#ifndef DARIO
		i_player->SetContract(price, years);
#endif
		m_budget -= price;
		m_sp->GetPeopleMgr().Ingage(i_player, true);
	}

	return result;
}

bool Team::SellPlayer(People* i_player)
{
	assert(i_player);

	const int value = i_player->SellValue();
	const bool result = RemovePeople(i_player);

	if(result)
	{
		// update the budget
		DARIONLY(m_budget += value;)
		
		//penal if player just has a contract
		NOTDARIONLY(if(i_player->HasContract())m_budget -= value;)

		m_sp->GetPeopleMgr().Sell(i_player, true);
	}

	return result;
}

#ifndef DARIO
bool Team::ContractExpires(People* i_player, const bool i_isPlayer)
{
	assert(i_player);

	const bool result = RemovePeople(i_player);

	//TODO check if the number of player is too low

	if(result)
	{
		m_sp->GetPeopleMgr().Sell(i_player, i_isPlayer);
	}	
	
	return result;
}
#endif

void Team::SwapPlayers(int i_first, int i_second)
{
	if(i_first != i_second)
		STL::swap(m_players.at(i_first), m_players.at(i_second));
}

int Team::GetTeamPoints(const int i_numYears) const
{
	const size_t scoreSize = m_teamPoints.size();
	size_t numYears = (i_numYears <= 0 ? scoreSize : i_numYears);
	numYears = STL::min(numYears, scoreSize);

	int sum = 0;
	for(size_t i= scoreSize-numYears; i< scoreSize; ++i)
		sum += m_teamPoints[i];

	return sum;
}

void Team::AddTeamPoints(const int i_points)
{
	m_teamPoints.push_back(i_points);
}

void Team::ClearTeamPoints()
{
	m_teamPoints.clear();
}

size_t Team::CalculateTotalSalary() const
{
	size_t totalSalary = 0;
	for(PeopleVecCit it = m_players.begin(); it != m_players.end(); ++it)
		totalSalary += (*it)->GetSalary();
	return totalSalary;
}

#ifndef DARIO
void Team::CheckRenewContract(STD::stringstream& o_message)
{
	//TODO
	PeopleVec thePlayers = GetPlayersList();

	for(PeopleVecIt it = thePlayers.begin(); it < thePlayers.end(); ++it)
	{
		if((*it)->GetContract()->m_years <= 1)
		{
			const int salary = (*it)->CalculateExpectedSalary();
			(*it)->SetContract(salary, Rand()%5+1);
			o_message << GetName() <<  " ha rinnovato il contratto di " << (*it)->GetName() << " per " << (*it)->GetContract()->m_years << " anni a " << (*it)->GetContract()->m_salary << " banane a partita" << STD::endl;
		}
	}

}
#endif

void Team::EndTurnUpdate()
{
	// update budget
	m_budget -= CalculateTotalSalary();
}

int	Team::GetTeamPower() const
{
	int power = 0;
	for (PeopleVecCit It = m_players.begin(); It != m_players.end(); ++It)
		power += (*It)->GetSkills().m_playerSkills.GetAverageSkills();
	return power;
}

int Team::GetRoleByIndex(int i_index) const
{
	if(i_index < TITOLAR_PER_ROLE_BY_FORMATION[m_formation][GoalkeeperRole])
		return GoalkeeperRole;
	
	i_index -= TITOLAR_PER_ROLE_BY_FORMATION[m_formation][GoalkeeperRole];
	if(i_index < TITOLAR_PER_ROLE_BY_FORMATION[m_formation][DefenderRole])
		return DefenderRole;

	i_index -= TITOLAR_PER_ROLE_BY_FORMATION[m_formation][DefenderRole];
	if(i_index < TITOLAR_PER_ROLE_BY_FORMATION[m_formation][MidfieldRole])
		return MidfieldRole;

	i_index -= TITOLAR_PER_ROLE_BY_FORMATION[m_formation][MidfieldRole];
	if(i_index < TITOLAR_PER_ROLE_BY_FORMATION[m_formation][AttackerRole])
		return AttackerRole;

	return NUM_FORMATION_ROLES;
}

int Team::GetTrainedRoles(const People* const i_trainer) const
{
	int trainedRoles = TrNone;
	for (size_t k=0; k<Team::NUM_TRAINERS_ROLES; ++k)
		if(m_trainerRoles[k] == i_trainer)
			trainedRoles |= (1<<k);
	return trainedRoles;
}

ZStream& Team::Serialize(ZStream& o_stream) const
{
	o_stream.WriteOpenTag("Team");
	o_stream.WriteAttribute("ID", m_id);
	o_stream.WriteAttribute("LeagueID", m_leagueID);
	o_stream.WriteAttribute("Name", m_name);
	o_stream.WriteAttribute("Budget", m_budget);
	o_stream.WriteAttribute("BankruptWarnings", m_bankruptWarnings);
	o_stream.WriteAttribute("Formation", m_formation);
	o_stream.WriteAttribute("PlayersNum",	m_players.size());
	for(PeopleVecCit it = m_players.begin(); it != m_players.end(); ++it)
		o_stream.WriteAttribute("PlayerID", (*it)->GetId());

	o_stream.WriteAttribute("TrainersNum",	m_trainers.size());
	for(PeopleVecCit it = m_trainers.begin(); it != m_trainers.end(); ++it)
		o_stream.WriteAttribute("TrainerID", (*it)->GetId());

	o_stream.WriteAttribute("TeamPointsNum", m_teamPoints.size());
	for(IntVecCit it = m_teamPoints.begin(); it != m_teamPoints.end(); ++it)
		o_stream.WriteAttribute("Points", (*it));

	m_teamHistory.Serialize(o_stream);

	o_stream.WriteCloseTag("Team");
	
	return o_stream;
}

ZStream& Team::Deserialize(ZStream& i_stream)
{
	i_stream.SkipLine();
	i_stream.ReadAttribute(m_id);
	i_stream.ReadAttribute(m_leagueID);
	i_stream.ReadAttribute(m_name);
	m_sp->GetNameMgr().SetTeamNameUsed(m_name);
	i_stream.ReadAttribute(m_budget);
	i_stream.ReadAttribute(m_bankruptWarnings);
	i_stream.ReadAttribute(m_formation);

	int theID;
	size_t theNum;

	i_stream.ReadAttribute(theNum);
	m_players.resize(theNum);
	for(size_t i = 0; i < theNum; ++i)
	{
		i_stream.ReadAttribute(theID);
		m_players[i] = m_sp->GetPeopleMgr().GetPeopleFromID(theID, StatusPlayer);
	}

	i_stream.ReadAttribute(theNum);
	m_trainers.resize(theNum);
	for(size_t i = 0; i < theNum; ++i)
	{
		i_stream.ReadAttribute(theID);
		m_trainers[i] = m_sp->GetPeopleMgr().GetPeopleFromID(theID, StatusTrainer);
	}

	for(size_t i = 0; i < Team::NUM_TRAINERS_ROLES; ++i)
		m_trainerRoles[i] = 0;

	i_stream.ReadAttribute(theNum);
	m_teamPoints.resize(theNum);
	for(size_t i = 0; i < theNum; ++i)
		i_stream.ReadAttribute(m_teamPoints[i]);

	m_teamHistory.Deserialize(i_stream);

	i_stream.SkipLine();

	return i_stream;
}
